NGINX & PHP-FPM Security Configuration Guide

Complete Server Configuration for WordPress Hosting

angelscript

Table of Contents

1. NGINX Logs Management

NGINX logs are essential for monitoring server activity, troubleshooting issues, and analyzing traffic patterns. Logs are typically stored in the /var/log/nginx directory.

Viewing Log Files

Navigate to the NGINX logs directory:

cd /var/log/nginx

List all log files:

ls

View the entire log file:

sudo cat log_file_name.log

View log file with pagination (recommended for large files):

sudo less log_file_name.log
Tip: Use less for large log files as it allows you to navigate through the file efficiently. Press 'q' to quit, 'Space' to scroll down, and '/' to search.

2. PHP-FPM Pool Configuration

PHP-FPM (FastCGI Process Manager) pools allow you to run multiple PHP applications with different configurations and security settings on the same server.

PHP-FPM Pool Architecture

NGINX Web Server
PHP-FPM Master Process
Pool 1 (example.com)
Pool 2 (site2.com)
Pool 3 (site3.com)

User and Group Management

Create a new user for the PHP pool:

sudo useradd username

Add users to groups (multiple variations):

sudo usermod -a -G username_group www-data
sudo usermod -a -G www-data username_group
sudo usermod -a -G username_group $USER

Creating a New PHP Pool

Navigate to the PHP-FPM pool directory:

cd /etc/php/8.3/pool.d/

List existing pools:

ls

Copy the default pool configuration:

sudo cp www.conf example.com.conf

Edit the new pool configuration:

sudo nano example.com.conf

Pool Configuration File

; Start a new pool named 'example' [example] ; Unix user/group of the child processes user = username group = username ; The address on which to accept FastCGI requests listen = /run/php/php8.3-fpm-example.com.sock ; Set open file descriptor rlimit rlimit_files = 15000 ; Set max core size rlimit rlimit_core = 100 ; PHP configuration flags php_flag[display_errors] = off php_admin_value[error_log] = /var/log/fpm-php.www.log php_admin_flag[log_errors] = on ;php_admin_value[memory_limit] = 32M
angelscript

Creating the FPM Log File

Create the log file:

sudo touch /var/log/fpm-php.example.com.log

Set ownership:

sudo chown user:www-data /var/log/fpm-php.example.com.log

Set permissions:

sudo chmod 660 /var/log/fpm-php.example.com.log

Enabling the New Pool

Reload PHP-FPM to apply changes:

sudo systemctl reload php8.3-fpm

Verify the socket configuration:

sudo grep "listen = /" example.conf

Configuring NGINX to Use the Pool

Edit your NGINX site configuration:

sudo nano /etc/nginx/sites-available/example.com.conf

Update the FastCGI pass directive:

fastcgi_pass unix:/run/php/php8.3-fpm-example.com.sock;

Test NGINX configuration:

sudo nginx -t

Reload NGINX:

sudo systemctl reload nginx

Setting File Permissions

Set ownership recursively:

sudo chown -R username:username public_html/

Set directory permissions (770):

sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \;

List files to verify:

ls -l

Set file permissions (660):

sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \;

Testing PHP Configuration

Create a PHP info file:

cd public_html/
sudo nano public_html/filename.php
<?php phpinfo(); ?>
angelscript

PHP Configuration Options

Enable URL file opening:

php_admin_flag[allow_url_fopen] = on

Reload PHP-FPM after changes:

sudo systemctl reload php8.3-fpm

Disabling Dangerous Functions

; ENABLED FUNCTIONS ; disk_free_space ; DISABLED FUNCTIONS php_admin_value[disable_functions] = shell_exec, opcache_get_configuration, opcache_get_status, disk_total_space, diskfreespace, dl, exec, passthru, pclose, pcntl_alarm, pcntl_exec, pcntl_fork, pcntl_get_last_error, pcntl_getpriority, pcntl_setpriority, pcntl_signal, pcntl_signal_dispatch, pcntl_sigprocmask, pcntl_sigtimedwait, pcntl_sigwaitinfo, pcntl_strerror, pcntl_waitpid, pcntl_wait, pcntl_wexitstatus, pcntl_wifcontinued, pcntl_wifexited, pcntl_wifsignaled, pcntl_wifstopped, pcntl_wstopsig, pcntl_wtermsig, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, show_source, system
angelscript

3. Open Base Dir Security Configuration

Security Notice: Open Base Dir is a critical security measure that restricts PHP file access to specific directories, preventing unauthorized access to sensitive system files.

Understanding Open Base Dir

Open Base Dir is a PHP security feature that:

Open Base Dir Access Control

✓ Allowed Directories

  • /var/www/example.com/public_html/
  • /var/www/example.com/tmp/

✗ Blocked Directories

  • /etc/
  • /tmp/
  • /var/log/
  • Other system directories

The Problem with /tmp Directory

Security Risk: Including the system /tmp directory in Open Base Dir can expose sensitive information and allow malicious code execution.

Risks of Including System /tmp:

The Solution: Custom Temporary Directories

Create site-specific temporary directories instead of using the system /tmp:

Step 1: Create Custom tmp Directory

Navigate to your site directory:

cd /var/www/example.com/

Check current directory structure:

ls -l

Create the tmp directory:

sudo mkdir tmp/

Set ownership:

sudo chown username:username tmp/

Set permissions:

sudo chmod 770 tmp/

Verify the changes:

ls -l

Step 2: Configure PHP Upload and Temp Directories

Navigate to PHP pool configuration:

cd /etc/php/8.3/fpm/pool.d/

Edit your site's pool configuration:

sudo nano example.com.conf

Add the following directives at the end of the file:

; Custom upload and temporary directories php_admin_value[upload_tmp_dir] = /var/www/example.com/tmp/ php_admin_value[sys_temp_dir] = /var/www/example.com/tmp/ ; Open Base Dir restriction php_admin_value[open_basedir] = /var/www/example.com/public_html/:/var/www/example.com/tmp/
angelscript
Configuration Explanation:
  • upload_tmp_dir: Specifies where PHP stores uploaded files temporarily
  • sys_temp_dir: Defines where PHP stores temporary files during script execution
  • open_basedir: Restricts PHP access to only the specified directories (separated by colons)

Step 3: Apply Configuration

Reload PHP-FPM to enable the changes:

sudo systemctl reload php8.3-fpm

Performance Considerations

Potential Overhead

  • Additional file system checks
  • Slight performance impact on file operations
  • Increased CPU usage on high-traffic sites

Mitigation Strategies

  • Implement caching mechanisms
  • Use opcode caching (OPcache)
  • Optimize file operations
  • Security benefits outweigh minimal overhead

Testing the Configuration

Testing Steps:
  1. Log into your WordPress dashboard
  2. Navigate to Media → Add New
  3. Upload an image file
  4. Verify successful upload to Media Library
  5. Test WordPress core updates

4. SSL Certificate Configuration

SSL/TLS certificates encrypt data between the server and client, ensuring secure communication. Let's Encrypt provides free SSL certificates.

Installing Certbot

Update package lists:

sudo apt update

Install Certbot with Cloudflare DNS plugin:

sudo apt install certbot python3-certbot-dns-cloudflare

Obtaining SSL Certificates

Using webroot authentication:

sudo certbot certonly --webroot -w /var/www/example.com/public_html/ -d example.com -d www.example.com

Generating Diffie-Hellman Parameters

Create SSL directory:

cd /etc/nginx/
sudo mkdir ssl/

Navigate to SSL directory:

cd ssl/

Generate DH parameters (this may take several minutes):

sudo openssl dhparam -out dhparam.pem 2048

Verify creation:

ls

Creating SSL Configuration Files

Site-Specific SSL Certificate Configuration

Create certificate configuration file:

sudo nano /etc/nginx/ssl/ssl_certs_example.com.conf
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
angelscript

Global SSL Configuration

Create global SSL settings file:

sudo nano /etc/nginx/ssl/ssl_all_sites.conf
# CONFIGURATION RESULTS IN A+ RATING AT SSLLABS.COM LAST UPDATED: MAY 2025 SSL CACHING AND PROTOCOLS ssl_session_cache shared:SSL:20m; ssl_session_timeout 180m; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; Cipher suite - must be on a single line ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305; ssl_dhparam /etc/nginx/ssl/dhparam.pem; SSL STAPLING DISABLED (Let's Encrypt removed support May 2025) #ssl_stapling on; #ssl_stapling_verify on; DNS resolver set to Cloudflare resolver 1.1.1.1 1.0.0.1; resolver_timeout 15s; ssl_session_tickets off; HSTS HEADERS add_header Strict-Transport-Security "max-age=31536000;" always; After setting up ALL subdomains, use this instead: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;" always; Enable QUIC and HTTP/3 ssl_early_data on; add_header Alt-Svc 'h3=":$server_port"; ma=86400'; add_header x-quic 'H3'; quic_retry on;
angelscript
Important: The ssl_ciphers directive must be on a single line. Do not split it across multiple lines.

5. HTTP/3 and QUIC Setup

What is HTTP/3? HTTP/3 is the latest version of the HTTP protocol, built on top of QUIC (Quick UDP Internet Connections). It provides faster, more reliable connections, especially on unstable networks.

Configuring NGINX Server Blocks

HTTP to HTTPS Redirect

server { listen 80; server_name example.com www.example.com; # Permanent redirect to HTTPS return 301 https://example.com$request_uri; }
pgsql

Primary HTTPS Server Block (with reuseport)

server { # Standard HTTPS with HTTP/2 listen 443 ssl; http2 on; # QUIC and HTTP/3 with reuseport (first server only) listen 443 quic reuseport; http3 on; server_name example.com www.example.com; # Include SSL certificate configuration include /etc/nginx/ssl/ssl_certs_example.com.conf; include /etc/nginx/ssl/ssl_all_sites.conf; # Rest of your server configuration... }
pgsql

Additional HTTPS Server Blocks (without reuseport)

server { # Standard HTTPS with HTTP/2 listen 443 ssl; http2 on; # QUIC and HTTP/3 WITHOUT reuseport listen 443 quic; http3 on; server_name site2.example.com; # Include SSL configuration include /etc/nginx/ssl/ssl_certs_site2.example.com.conf; include /etc/nginx/ssl/ssl_all_sites.conf; # Rest of your server configuration... }
angelscript
Important: Use reuseport on only ONE server block. Additional server blocks should NOT include the reuseport parameter.

Testing Configuration

Test NGINX configuration:

sudo nginx -t

Reload NGINX:

sudo systemctl reload nginx

Testing HTTP/3 Functionality

Disable Browser Caching for Testing

Add this directive temporarily to your location block:

# PREVENTS BROWSER CACHING - USED TO TEST HTTP3 REMOVE THIS DIRECTIVE AFTER CONFIRMING HTTP3 IS ENABLED add_header Cache-Control 'no-cache,no-store';
angelscript

Command Line Testing

Test HTTP redirect:

curl -I http://example.com

Test HTTPS with www:

curl -I https://www.example.com

Test HTTPS without www:

curl -I https://example.com

Browser Testing

Steps to verify HTTP/3 in browser:
  1. Open Developer Console (F12)
  2. Go to Network tab
  3. Refresh the page
  4. Right-click on column headers
  5. Select "Protocol" to add the Protocol column
  6. Look for "h3" in the Protocol column

Additional Testing Tools

Online HTTP/3 testing tools:

Adding FastCGI Host Parameter

In your PHP processing location block, add:

location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.sock; include /etc/nginx/includes/fastcgi_optimize.conf; }
angelscript

6. SSL Certificate Management

Certbot Commands

View All Certificates

sudo certbot certificates

Delete a Certificate

sudo certbot delete

Renew All Certificates

sudo certbot renew

Force Certificate Renewal

sudo certbot renew --force-renewal

Automated Certificate Renewal

Set up automatic renewal using cron:

sudo crontab -e

Add the following lines to renew certificates twice monthly:

# Renew SSL certificates on the 14th and 28th of each month at 1:00 AM 00 1 14,28 * * certbot renew --force-renewal Reload NGINX on the 14th and 28th of each month at 2:00 AM 00 2 14,28 * * systemctl reload nginx
angelscript
Cron Format Explanation:
  • 00 - Minute (0)
  • 1 or 2 - Hour (1 AM or 2 AM)
  • 14,28 - Day of month (14th and 28th)
  • * - Every month
  • * - Any day of week

7. HTTP Security Headers

Security headers protect your website from various attacks and vulnerabilities by controlling browser behavior.

Creating Security Headers Configuration

Navigate to includes directory:

cd /etc/nginx/includes/

Create headers configuration file:

sudo nano http_headers.conf
# ------------------------------------------------------- Referrer-Policy - Controls how much referrer information is shared ------------------------------------------------------- #add_header Referrer-Policy "no-referrer"; add_header Referrer-Policy "strict-origin-when-cross-origin"; #add_header Referrer-Policy "unsafe-url"; X-Content-Type-Options - Prevents MIME type sniffing add_header X-Content-Type-Options "nosniff"; X-Frame-Options - Prevents clickjacking attacks add_header X-Frame-Options "sameorigin"; X-XSS-Protection - Enables browser XSS protection add_header X-XSS-Protection "1; mode=block"; Permissions-Policy - Controls browser features and APIs add_header Permissions-Policy 'accelerometer=(), camera=(), clipboard-read=(), clipboard-write=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), fullscreen=(self "https://www.youtube.com")';
angelscript

Including Headers in Site Configuration

Edit your site configuration:

cd /etc/nginx/sites-available/
sudo nano example.com.conf

Add this line ABOVE your PHP processing location block:

include /etc/nginx/includes/http_headers.conf;

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Security Headers Explained

Header Purpose Value
Referrer-Policy Controls referrer information sharing strict-origin-when-cross-origin
X-Content-Type-Options Prevents MIME type sniffing nosniff
X-Frame-Options Prevents clickjacking sameorigin
X-XSS-Protection Browser XSS filter 1; mode=block
Permissions-Policy Controls browser features Restricted access to APIs

Browser Caching with Security Headers

Create combined caching and security configuration:

sudo nano /etc/nginx/includes/browser_caching_security_headers.conf
# Browser caching configuration expires 30d; etag on; if_modified_since exact; add_header Pragma "public"; add_header Cache-Control "public, no-transform"; Fallback to index.php for WordPress try_files $uri $uri/ /index.php?$args; Include security headers include /etc/nginx/includes/http_headers.conf; Disable access logging for static files access_log off;
angelscript

Test and reload NGINX:

sudo nginx -t
sudo systemctl reload nginx

8. File Ownership and Permissions

Permission Models Comparison

Standard Permissions

Best for: Development environments

  • Directories: 770 (rwxrwx---)
  • Files: 660 (rw-rw----)
  • More flexible for updates

Hardened Permissions

Best for: Production environments

  • Directories: 550 (r-xr-x---)
  • Files: 440 (r--r-----)
  • Maximum security
  • wp-content remains writable

Standard Permissions (Development)

Navigate to site directory:

cd /var/www/example.com/

Set ownership:

sudo chown -R username:username public_html/

Set directory permissions (770):

sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \;

Set file permissions (660):

sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \;

Secure wp-config.php:

sudo chmod 400 public_html/wp-config.php

Hardened Permissions (Production)

Navigate to site directory:

cd /var/www/example.com/

Set ownership:

sudo chown -R username:username public_html/

Set read-only directory permissions (550):

sudo find /var/www/example.com/public_html/ -type d -exec chmod 550 {} \;

Set read-only file permissions (440):

sudo find /var/www/example.com/public_html/ -type f -exec chmod 440 {} \;

Make wp-content writable for uploads/updates:

sudo find /var/www/example.com/public_html/wp-content/ -type d -exec chmod 770 {} \;
sudo find /var/www/example.com/public_html/wp-content/ -type f -exec chmod 660 {} \;

Permission Values Explained

Value Binary Permissions Description
7 111 rwx Read, Write, Execute
6 110 rw- Read, Write
5 101 r-x Read, Execute
4 100 r-- Read only
0 000 --- No permissions

9. NGINX Security Directives (WordPress Firewall)

WordPress-Safe Firewall: This ruleset is based on the 8G Firewall with low false-positive risks, updated for December 2026.

Creating Security Directives File

Navigate to includes directory:

cd /etc/nginx/includes/

Create security directives file:

sudo nano nginx_security_directives.conf
# WORDPRESS-SAFE NGINX 8G (based) FIREWALL Ruleset Low false-positive risks Updated December 2026 Disable favicon logging location = /favicon.ico { access_log off; log_not_found off; } Deny access to sensitive core and config files location = /wp-config.php { deny all; } location = /wp-admin/install.php { deny all; } location ~* ^/(readme|license|licence).(txt|html)$ { deny all; } location ~* .ini$ { deny all; } Harden WP core location ~* ^/wp-includes/[^/]+.php$ { deny all; } location ~* ^/wp-includes/js/tinymce/langs/.+.php$ { deny all; } location ~* ^/wp-includes/theme-compat/ { deny all; } Prevent PHP execution in uploads, themes, and plugins location ~* ^/wp-content/uploads/..(php[1-8]?|pht|phtml?|phps)$ { deny all; } location ~ ^/wp-content/plugins/..(php[1-8]?|pht|phtml?|phps)$ { deny all; } location ~ ^/wp-content/themes/.*.(php[1-8]?|pht|phtml?|phps)$ { deny all; } Protect upgrade and backup directories location ~* ^/wp-content/(upgrade|backup-.)/..(php[1-8]?|pht|phtml?|phps)$ { deny all; } Block development and dependency files/directories location ~* (composer.(json|lock)|package.json|yarn.lock|/vendor/|/node_modules/) { deny all; } Block dangerous or unused HTTP methods if ($request_method ~* ^(TRACE|DELETE|TRACK)$) { return 403; } Block known vulnerability scanners if ($http_user_agent ~* (nikto|sqlmap|masscan|nmap|dirbuster|acunetix|openvas)) { return 444; }
angelscript

Including Security Directives

Edit your site configuration:

cd /etc/nginx/sites-available/
sudo nano example.com.conf

Add ABOVE your PHP processing location block:

include /etc/nginx/includes/nginx_security_directives.conf;

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Allowing PHP Execution for Specific Plugins

Special Case: Some plugins require PHP execution in the plugins directory. By default, this is blocked for security.

Testing Blocked Execution

Create a test PHP file:

cd /var/www/example.com/
sudo nano public_html/wp-content/plugins/test556.php
<?php phpinfo(); ?>
vim

Access in browser: https://example.com/wp-content/plugins/test556.php

Result: 403 Forbidden (PHP execution blocked)

Allowing Specific Plugin PHP Execution

If a plugin needs to execute PHP, add a specific location block AFTER your main PHP processing block:

location = /wp-content/plugins/test556.php { allow all; include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.sock; include /etc/nginx/includes/fastcgi_optimize.conf; }
angelscript

Test and reload:

sudo nginx -t
sudo systemctl reload nginx && sudo systemctl restart php8.3-fpm

Clean up test file:

cd /var/www/example.com/public_html/wp-content/plugins/
sudo rm test556.php
Best Practice: Only whitelist specific PHP files that absolutely require execution. This maintains maximum security while allowing necessary functionality.

10. Rate Limiting

Rate limiting protects your server from brute force attacks and excessive requests to sensitive endpoints.

Rate Limiting Flow

Client Request
NGINX Rate Limit Zone Check
Within Limit → Allow
Exceeds Limit → Block (444)

Configuring Rate Limit Zone

Edit main NGINX configuration:

cd /etc/nginx/
sudo nano nginx.conf

Add in the http context (before server blocks):

## Rate Limiting Zone: wp | Size: 10MB | Rate: 30 requests per minute limit_req_zone $binary_remote_addr zone=wp:10m rate=30r/m;
angelscript
Parameters Explained:
  • $binary_remote_addr: Client IP address (binary format for efficiency)
  • zone=wp:10m: Memory zone named "wp" with 10MB allocation
  • rate=30r/m: Maximum 30 requests per minute per IP

Creating Site-Specific Rate Limiting

Navigate to includes directory:

cd /etc/nginx/includes/

Create rate limiting configuration:

sudo nano rate_limiting_example.com.conf
# Rate limiting for wp-login.php (prevent brute force) location = /wp-login.php { limit_req zone=wp burst=20 nodelay; limit_req_status 444; include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.sock; include /etc/nginx/includes/fastcgi_optimize.conf; } Rate limiting for xmlrpc.php (prevent DDoS) location = /xmlrpc.php { limit_req zone=wp burst=20 nodelay; limit_req_status 444; include snippets/fastcgi-php.conf; fastcgi_param HTTP_HOST $host; fastcgi_pass unix:/run/php/php8.3-fpm-example.sock; include /etc/nginx/includes/fastcgi_optimize.conf; }
angelscript
Important: Replace unix:/run/php/php8.3-fpm-example.sock with your actual PHP-FPM socket path.

Rate Limiting Parameters Explained

Parameter Description Value
limit_req zone References the rate limit zone wp
burst Allows temporary burst of requests 20 requests
nodelay Process burst requests immediately -
limit_req_status HTTP status code for blocked requests 444 (connection closed)

Including Rate Limiting in Site Configuration

Edit your site configuration:

sudo nano /etc/nginx/sites-available/example.com.conf

Add inside your server block:

# Rate Limiting Include include /etc/nginx/includes/rate_limiting_example.com.conf;
angelscript

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Why Rate Limit These Endpoints?

wp-login.php

  • Primary target for brute force attacks
  • Unlimited attempts can compromise site
  • Slows down automated attack tools

xmlrpc.php

  • Used for DDoS amplification attacks
  • Allows multiple authentication attempts
  • Can consume server resources

11. Additional WordPress Security

Disabling File Modifications from Dashboard

For maximum security, prevent file modifications through the WordPress dashboard:

Edit wp-config.php:

sudo nano /var/www/example.com/public_html/wp-config.php

Add this line before "That's all, stop editing!":

define('DISALLOW_FILE_MODS', true);

Reload PHP-FPM:

sudo systemctl reload php8.3-fpm
What this does: Disables theme/plugin installation, updates, and editing through the WordPress admin panel. All changes must be done via command line or SFTP.

12. Database Security and Privileges

Proper database privilege management is crucial for security. Apply the principle of least privilege.

Recommended Database Privileges

Revoke All Existing Privileges

REVOKE ALL PRIVILEGES ON site_db.* FROM 'site_user'@'hostname';

Grant Minimum Required Privileges

GRANT SELECT, INSERT, UPDATE, DELETE ON site_db.* TO 'site_user'@'hostname';

Apply changes:

FLUSH PRIVILEGES;

Additional Privileges (When Needed)

For plugins that require table creation or modification:

GRANT CREATE, ALTER, INDEX ON database_name.* TO 'username'@'localhost';
Security Note: Only grant CREATE, ALTER, and INDEX privileges temporarily when installing/updating plugins that require them. Revoke these privileges afterward.

Database Privilege Levels Explained

Privilege Purpose Required?
SELECT Read data from tables ✓ Always
INSERT Add new rows to tables ✓ Always
UPDATE Modify existing data ✓ Always
DELETE Remove rows from tables ✓ Always
CREATE Create new tables Only during installation/updates
ALTER Modify table structure Only during installation/updates
INDEX Create/delete indexes Only during installation/updates
DROP Delete tables ✗ Not recommended

Best Practices

Production Sites

  • Grant only SELECT, INSERT, UPDATE, DELETE
  • Use separate admin user for maintenance
  • Regularly audit database privileges

Development Sites

  • Can include CREATE, ALTER, INDEX
  • Still avoid DROP privilege
  • Use different credentials than production

13. Quick Reference Commands

Service Management

sudo systemctl reload nginx
sudo systemctl restart nginx
sudo systemctl reload php8.3-fpm
sudo systemctl restart php8.3-fpm
sudo systemctl status nginx
sudo systemctl status php8.3-fpm

Testing and Validation

sudo nginx -t
curl -I https://example.com
sudo certbot certificates

Log Monitoring

sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/fpm-php.example.com.log

Permissions Quick Commands

sudo chown -R username:username /var/www/example.com/public_html/
sudo find /var/www/example.com/public_html/ -type d -exec chmod 770 {} \;
sudo find /var/www/example.com/public_html/ -type f -exec chmod 660 {} \;

14. Security Checklist

✓ Configuration Checklist

  • [ ] PHP-FPM pools configured per site
  • [ ] Open Base Dir configured with custom tmp directory
  • [ ] SSL certificates installed and auto-renewal configured
  • [ ] HTTP/3 and QUIC enabled
  • [ ] Security headers configured
  • [ ] File permissions set (hardened for production)
  • [ ] WordPress security directives implemented
  • [ ] Rate limiting configured for login and XMLRPC
  • [ ] Database privileges minimized
  • [ ] DISALLOW_FILE_MODS enabled in wp-config.php
  • [ ] wp-config.php permissions set to 400
  • [ ] Dangerous PHP functions disabled
  • [ ] SSL Labs rating: A+
  • [ ] HTTP/3 verified as working

15. Troubleshooting Common Issues

502 Bad Gateway

  • Check PHP-FPM is running
  • Verify socket path in NGINX config
  • Check PHP-FPM error logs
  • Ensure correct pool user/group

403 Forbidden

  • Check file/directory permissions
  • Verify ownership settings
  • Review security directives
  • Check Open Base Dir configuration

File Upload Failures

  • Verify custom tmp directory exists
  • Check tmp directory permissions (770)
  • Confirm upload_tmp_dir in pool config
  • Review wp-content permissions

Rate Limiting Issues

  • Adjust burst value if needed
  • Check rate limit zone in nginx.conf
  • Review access logs for 444 status
  • Consider whitelisting admin IPs

16. Performance Optimization Tips

NGINX Optimization

PHP-FPM Optimization

WordPress Optimization